/*
 * Desc： 消息摘要算法（验证数据一致性）
 *  1、MD
 *  2、SHA
 *  3、MAC(需要密钥)
 *
 * @Author jiaxin
 * @Company 亚信科技
 * @Since 2020/7/25 15:42
 *
 */
package com.ybzl.common.crypto;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.util.Base64;

public class MessageDigestUtil {

    public static String md2(String src) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD2");
            byte[] result = messageDigest.digest(src.getBytes());
            return Hex.toHexString(result);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    /***
     *  Md4编码，需要bc的支持
     * @param src 源数据
     * @return 编码值
     */
    public static String md4(String src) {
        try {
            // 增加一个bc的provider
            Security.addProvider(new BouncyCastleProvider());
            MessageDigest messageDigest = MessageDigest.getInstance("MD4");
            byte[] result = messageDigest.digest(src.getBytes());
            return Hex.toHexString(result);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String md5(String src) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            byte[] result = messageDigest.digest(src.getBytes());
            // 将加密后的byte数组进行16进制编码，便于输出。直接new String()出来的对象会乱码，因为该对象本身并不是字符串数据的另一种表达方式
            return Hex.toHexString(result);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String sha1(String src) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
            byte[] result = messageDigest.digest(src.getBytes());
            return Hex.toHexString(result);

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String sha224(String src) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-224");
            byte[] result = messageDigest.digest(src.getBytes());
            return Hex.toHexString(result);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String sha256(String src) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            byte[] result = messageDigest.digest(src.getBytes());
            return Hex.toHexString(result);

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String sha384(String src) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-384");
            byte[] result = messageDigest.digest(src.getBytes());
            return Hex.toHexString(result);

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String sha512(String src) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-512");
            byte[] result = messageDigest.digest(src.getBytes());
            return Hex.toHexString(result);

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    /***
     *  Mac消息摘要算法需要同md,sha的区别是需要密钥
     * @param src
     * @return
     */
    public static String hmacMd5(String src) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
            SecretKey secretKey = keyGenerator.generateKey();

            // 输出密钥
            byte[] keyBytes = secretKey.getEncoded();
            System.out.println("HmacMD5 密钥：" + Base64.getEncoder().encodeToString(keyBytes));
            // 还原密钥
            //SecretKey secretKey = new SecretKeySpec(keyBytes,"HmacMD5");
            // 构建MAC摘要算法
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            // 初始化 MAC
            mac.init(secretKey);
            byte[] result = mac.doFinal(src.getBytes());
            return Hex.toHexString(result);

        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String hmacSHA256(String src) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA256");
            SecretKey secretKey = keyGenerator.generateKey();

            // 输出密钥
            byte[] keyBytes = secretKey.getEncoded();
            System.out.println("HmacSHA256 密钥：" + Base64.getEncoder().encodeToString(keyBytes));
            // 还原密钥
            //SecretKey secretKey = new SecretKeySpec(keyBytes,"HmacMD5");
            // 构建MAC摘要算法
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            // 初始化 MAC
            mac.init(secretKey);
            byte[] result = mac.doFinal(src.getBytes());
            return Hex.toHexString(result);

        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String hmacSHA512(String src) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA512");
            SecretKey secretKey = keyGenerator.generateKey();

            // 输出密钥
            byte[] keyBytes = secretKey.getEncoded();
            System.out.println("HmacSHA512 密钥：" + Base64.getEncoder().encodeToString(keyBytes));
            // 还原密钥
            //SecretKey secretKey = new SecretKeySpec(keyBytes,"HmacMD5");
            // 构建MAC摘要算法
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            // 初始化 MAC
            mac.init(secretKey);
            byte[] result = mac.doFinal(src.getBytes());
            return Hex.toHexString(result);

        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 验证文件摘要合法性（指纹）
     *
     * @param filePath  待校验文件路径
     * @param algorithm 摘要算法
     * @param sign      摘要值
     * @return 验证结果
     */
    public static boolean verify4File(String filePath, String algorithm, String sign) {
        try {
            FileInputStream fis = new FileInputStream(filePath);
            DigestInputStream dis = new DigestInputStream(fis, MessageDigest.getInstance(algorithm));
            return verify(dis, sign);
        } catch (NoSuchAlgorithmException | IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    private static boolean verify(DigestInputStream dis, String sign) {
        try {
            int buf = 1024;
            byte[] buffer = new byte[1024];
            int read = dis.read(buffer, 0, buf);

            while (read > -1) {
                read = dis.read(buffer, 0, buf);
            }
            dis.close();
            MessageDigest messageDigest = dis.getMessageDigest();
            byte[] b = messageDigest.digest();
            return sign.equals(Hex.toHexString(b));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}
